﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Documents
//  File name:	LookupFieldWithPickerEditor.ascx.cs
//  Developer:	Solomatov Igor
//  Created:	03.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Smart;
using Smart.Common;
using Smart.Documents.Fields.LookupFieldWithPicker;

// ReSharper disable CheckNamespace

namespace iLoveSharePoint.Fields
// ReSharper restore CheckNamespace
{
	public partial class LookupFieldWithPickerEditor : UserControl, IFieldEditor
	{
		// ReSharper disable InconsistentNaming
		protected LookupFieldWithPicker lookupFieldWithPicker;

		protected SPWeb lookupWeb;
		protected SPList lookupList;

		private readonly List<Exception> exceptions = new List<Exception>();
		// ReSharper restore InconsistentNaming

		private void FillWebDropDown()
		{
			foreach (var web in WebsForCurrentUser)
			{
				dropDownListLookupWeb.Items.Add(new ListItem(web.Title, web.ID.ToString()));
			}
		}

		private List<SPWeb> _websForCurrentUser;

		private List<SPWeb> WebsForCurrentUser
		{
			get
			{
				if (_websForCurrentUser == null)
				{
					_websForCurrentUser = new List<SPWeb>();

					SPSecurity.RunWithElevatedPrivileges(
						delegate
						{
							using (var site = new SPSite(SPContext.Current.Site.ID))
							{
								foreach (SPWeb web in site.AllWebs)
								{
									if (web.DoesUserHavePermissions(SPContext.Current.Web.CurrentUser.LoginName, SPBasePermissions.Open))
										_websForCurrentUser.Add(SPContext.Current.Site.OpenWeb(web.ID));

									web.Dispose();
								}
							}
						});
				}

				return _websForCurrentUser;
			}
		}

		private void FillLookupListDropDown()
		{
			dropDownListLookupList.Items.Clear();

			foreach (SPList list in lookupWeb.Lists)
			{
				if (list.Hidden)
					continue;

				dropDownListLookupList.Items.Add(new ListItem(list.Title, list.ID.ToString()));
			}
		}

		private void FillLookupFieldDropDown()
		{
			dropDownListLookupField.Items.Clear();

			foreach (SPField field in lookupList.Fields)
			{
				if (!CanBeUsedAsDependentLookupField(field))
					continue;

				dropDownListLookupField.Items.Add(CreateListItemFromField(field));
			}
		}

		private void FillSearchableColumnsCheckBoxList()
		{
			checkBoxListSearchableColumns.Items.Clear();

			foreach (SPField field in lookupList.Fields)
			{
				if (!CanBeUsedAsSearchableField(field))
					continue;

				checkBoxListSearchableColumns.Items.Add(CreateListItemFromField(field));
			}
		}

		private void FillProjectionColumnsCheckBoxList()
		{
			checkBoxListProjectedColumns.Items.Clear();

			foreach (SPField field in lookupList.Fields)
			{
				if (!CanBeUsedAsDependentLookupField(field))
					continue;

				checkBoxListProjectedColumns.Items.Add(CreateListItemFromField(field));
			}
		}

		public bool DisplayAsNewSection
		{
			get { return false; }
		}

		public void InitializeWithField(SPField field)
		{
			lookupFieldWithPicker = field as LookupFieldWithPicker;

			//HACK: Delete dependent lookup fields when the delete button has been clicked
			if (lookupFieldWithPicker != null && Page.Request["__EVENTTARGET"] != null && Page.Request["__EVENTTARGET"].Contains("onetidDeleteItem"))
			{
				var dependentFieldNames = lookupFieldWithPicker.GetDependentLookupInternalNames();

				foreach (var dependentFieldName in dependentFieldNames)
				{
					lookupFieldWithPicker.fields.Delete(dependentFieldName);
				}
			}

			// It is not supported to change the lookup web or lookup list after the field has been created
			if (lookupFieldWithPicker != null)
			{
				dropDownListLookupWeb.Enabled = false;
				dropDownListLookupList.Enabled = false;
			}

			// If the field is being edited, verify that the lookup web still exists
			if (lookupFieldWithPicker != null && WebsForCurrentUser.All(w => w.ID != lookupFieldWithPicker.LookupWebId))
			{
				//TODO: Extract the error message into a ressource file
				exceptions.Add(new FileNotFoundException("Either you don't have permissions to the lookup site or the site has been deleted"));
				return;
			}

			if (!IsPostBack)
			{
				// Fill the webs intially
				FillWebDropDown();

				// Select Web
				dropDownListLookupWeb.SelectedValue =
					lookupFieldWithPicker == null
						? SPContext.Current.Web.ID.ToString()
						: lookupFieldWithPicker.LookupWebId.ToString();
			}

			lookupWeb = WebsForCurrentUser.First(w => w.ID == new Guid(dropDownListLookupWeb.SelectedValue));

			// If the field is being edited, verify that the lookup list still exists 
			if (lookupFieldWithPicker != null &&
				lookupFieldWithPicker.LookupList.ToUpper() != "SELF" &&
				((lookupFieldWithPicker.LookupList.IsGuid() && lookupWeb.Lists.OfType<SPList>().All(l => l.ID != new Guid(lookupFieldWithPicker.LookupList))) ||
				 (!lookupFieldWithPicker.LookupList.IsGuid() && lookupWeb.Lists.OfType<SPList>().All(l => l.GetUrl().Trim('/') != lookupFieldWithPicker.LookupList.Trim('/')))))
			{
				//TODO: Extract the error message into a ressource file
				exceptions.Add(new FileNotFoundException("Either you don't have permissions to the lookup list or the list has been deleted"));
				return;
			}

			if (!IsPostBack)
			{
				// Fill Lists
				FillLookupListDropDown();

				//Select List
				if (lookupFieldWithPicker == null)
				{
					dropDownListLookupList.SelectedIndex = 0;
				}
				else
				{
					dropDownListLookupList.Items.OfType<ListItem>().First(i => new Guid(i.Value) == new Guid(lookupFieldWithPicker.LookupList)).Selected = true;
				}
			}

			if (!IsPostBack)
			{
				lookupList = lookupWeb.Lists[new Guid(dropDownListLookupList.SelectedValue)];

				FillLookupFieldDropDown();
				FillSearchableColumnsCheckBoxList();
				FillProjectionColumnsCheckBoxList();

				if (lookupFieldWithPicker == null)
				{
					dropDownListLookupField.SelectedIndex = 0;
				}
				else
				{
					// verify that the main dependent field has not been deleted. 
					if (dropDownListLookupField.Items.OfType<ListItem>().Any(i => i.Value == lookupFieldWithPicker.LookupField))
					{
						//set the main dependent field as configured
						dropDownListLookupField.SelectedValue = lookupFieldWithPicker.LookupField;
						labelLookupFieldError.Visible = false;
					}
					else
					{
						labelLookupFieldError.Visible = true;
					}

					// select dependent fields 
					var dependentFieldNames = lookupFieldWithPicker.GetDependentLookupInternalNames();

					foreach (var dependentFieldName in dependentFieldNames)
					{
						var dependentField = (SPFieldLookup)lookupFieldWithPicker.fields.GetFieldByInternalName(dependentFieldName);

						var item =
							checkBoxListProjectedColumns.Items.OfType<ListItem>().FirstOrDefault(i => i.Value == dependentField.LookupField);

						if (item != null)
						{
							item.Selected = true;
						}
					}

					//select searchable fields
					var searchableFields = lookupFieldWithPicker.SearchableFields;

					foreach (var searchableFieldName in searchableFields)
					{
						var item =
							checkBoxListSearchableColumns.Items.OfType<ListItem>().FirstOrDefault(i => i.Value == searchableFieldName);

						if (item != null)
						{
							item.Selected = true;
						}
					}

					// select multi lookup
					checkBoxListMultiLookup.Checked = lookupFieldWithPicker.AllowMultipleValues;

					// fill Query textbox
					textboxQuery.Text = lookupFieldWithPicker.SearchQuery;
				}
			}

			if (IsPostBack || lookupFieldWithPicker == null)
				return;

			switch (lookupFieldWithPicker.RelationshipDeleteBehavior)
			{
				case SPRelationshipDeleteBehavior.None:
					radioButtonListRelationContraints.SelectedValue = "None";
					break;

				case SPRelationshipDeleteBehavior.Restrict:
					radioButtonListRelationContraints.SelectedValue = "Restrict";
					break;

				case SPRelationshipDeleteBehavior.Cascade:
					radioButtonListRelationContraints.SelectedValue = "Cascade";
					break;
			}
		}

		public void OnSaveChange(SPField field, bool isNewField)
		{
			lookupList = lookupWeb.Lists[new Guid(dropDownListLookupList.SelectedValue)];

			lookupFieldWithPicker = field as LookupFieldWithPicker;

			if (lookupFieldWithPicker == null)
				return;

			if (isNewField)
			{
				lookupFieldWithPicker.LookupWebId = lookupWeb.ID;
				lookupFieldWithPicker.LookupList = lookupList.ID.ToString();
			}

			lookupFieldWithPicker.LookupField = dropDownListLookupField.SelectedValue;
			lookupFieldWithPicker.AllowMultipleValues = checkBoxListMultiLookup.Checked;
			lookupFieldWithPicker.UnlimitedLengthInDocumentLibrary = true;
			lookupFieldWithPicker.SearchQuery = textboxQuery.Text.Trim();

			switch (radioButtonListRelationContraints.SelectedValue)
			{
				case "None":
					lookupFieldWithPicker.RelationshipDeleteBehavior = SPRelationshipDeleteBehavior.None;
					break;

				case "Restrict":
					lookupFieldWithPicker.RelationshipDeleteBehavior = SPRelationshipDeleteBehavior.Restrict;
					lookupFieldWithPicker.Indexed = true;
					break;

				case "Cascade":
					lookupFieldWithPicker.RelationshipDeleteBehavior = SPRelationshipDeleteBehavior.Cascade;
					lookupFieldWithPicker.Indexed = true;
					break;
			}

			lookupFieldWithPicker.SearchableFields =
				checkBoxListSearchableColumns.Items.OfType<ListItem>().Where(i => i.Selected).Select(i => i.Value).ToList();

			lookupFieldWithPicker.TempDependentLookups = checkBoxListProjectedColumns.Items.OfType<ListItem>().Where(i => i.Selected).Select(i => i.Value).ToList();

			//Hack: At this stage the control is not able to persist custom properties. Cache the current instance of the field. This instance will be needed later in the request cycle in the OnAdded and OnUpdated events
			HttpContext.Current.Items[typeof(LookupFieldWithPicker).Name] = lookupFieldWithPicker;
		}

		protected void OnLookupWebChanged(object sender, EventArgs eventArgs)
		{
			FillLookupListDropDown();

			dropDownListLookupList.SelectedIndex = 0;

			lookupList = lookupWeb.Lists[new Guid(dropDownListLookupList.SelectedValue)];

			FillLookupFieldDropDown();
			FillSearchableColumnsCheckBoxList();
			FillProjectionColumnsCheckBoxList();
		}

		protected void OnLookupListChanged(object sender, EventArgs eventArgs)
		{
			lookupList = lookupWeb.Lists[new Guid(dropDownListLookupList.SelectedValue)];

			FillLookupFieldDropDown();
			FillSearchableColumnsCheckBoxList();
			FillProjectionColumnsCheckBoxList();
		}

		protected override void OnPreRender(EventArgs e)
		{
			if (exceptions.Count == 0)
			{
				panelEditor.Visible = true;
				panelError.Visible = false;
			}
			else
			{
				panelEditor.Visible = true;
				panelError.Visible = true;
			}

			if (lookupFieldWithPicker != null)
			{
				labelVersion.Text =
					lookupFieldWithPicker.IsInCompatibilityMode
						? String.Format("{0} (Upgrade to {1} on save)", lookupFieldWithPicker.CurrentInstanceVersion, LookupFieldWithPicker.Version)
						: lookupFieldWithPicker.CurrentInstanceVersion.ToString();
			}
			else
			{
				labelVersion.Text = LookupFieldWithPicker.Version.ToString();
			}

			base.OnPreRender(e);
		}

		public static bool CanBeUsedAsSearchableField(SPField field)
		{
			return (field.Id == SPBuiltInFieldId.FileLeafRef || field.Hidden == false &&
					(field.Type == SPFieldType.Counter
					 || field.Type == SPFieldType.Boolean
					 || field.Type == SPFieldType.Integer
					 || field.Type == SPFieldType.Currency
					 || field.Type == SPFieldType.DateTime
					 || field.Type == SPFieldType.Number
					 || field.Type == SPFieldType.Text
					 || field.Type == SPFieldType.URL
					 || field.Type == SPFieldType.User
					 || field.Type == SPFieldType.Choice
					 || field.Type == SPFieldType.MultiChoice
					 || field.Type == SPFieldType.Lookup
					 || field.TypeAsString == "TaxonomyFieldTypeMulti"
					 || field.TypeAsString == "TaxonomyFieldType"
					 || (field.Type == SPFieldType.Calculated && ((SPFieldCalculated)field).OutputType == SPFieldType.Text))
				   );
		}

		private static bool CanBeUsedAsDependentLookupField(SPField field)
		{
			var canBeUsed = false;

			if (field.Hidden)
			{
				return false;
			}

			if (field.ParentList.HasExternalDataSource)
			{
				if (!field.InternalName.StartsWith("bdil", StringComparison.Ordinal) && (field.InternalName != "BdcIdentity"))
				{
					canBeUsed = true;
				}
			}

			if (((((field.Type == SPFieldType.Counter) || (field.Type == SPFieldType.Text)) || ((field.Type == SPFieldType.Number) || (field.Type == SPFieldType.DateTime))) ||
				 ((field.Type == SPFieldType.Computed) && ((SPFieldComputed)field).EnableLookup)) ||
				((field.Type == SPFieldType.Calculated) && (((SPFieldCalculated)field).OutputType == SPFieldType.Text)))
			{
				canBeUsed = true;
			}

			return canBeUsed;
		}

		private ListItem CreateListItemFromField(SPField field)
		{
			var item = new ListItem
						{
							Value = field.InternalName,
							Text =
								String.IsNullOrEmpty(field.AuthoringInfo)
									? field.Title
									: String.Format("{0} ({1})", field.Title, field.AuthoringInfo)
						};


			return item;
		}

		protected override void OnUnload(EventArgs e)
		{
			if (_websForCurrentUser != null)
			{
				_websForCurrentUser.ForEach(w => w.Dispose());
			}

			base.OnUnload(e);
		}
	}
}